package com.hk.core.autoconfigure.data.database;

import com.hk.commons.util.AssertUtils;
import com.hk.commons.util.CollectionUtils;
import com.hk.core.autoconfigure.data.PrincipalAccountResolverProvider;
import com.hk.core.autoconfigure.data.PrincipalTenantResolverProvider;
import com.hk.core.autoconfigure.data.RequestUriResolver;
import com.hk.core.autoconfigure.data.TenantIdResolverProvider;
import com.hk.core.jdbc.tenants.CompositeTenantIdResolver;
import com.hk.core.jdbc.tenants.DefaultTenantInfoAware;
import com.hk.core.jdbc.tenants.TenantIdResolver;
import com.hk.core.jdbc.tenants.UserTenantAware;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ClassUtils;

import javax.sql.DataSource;
import java.util.*;

/**
 * 多数据库配置
 * <p>
 * 注意: 多个数据库之前操作，这种配置不支持事物，如果需要使用事物，需要使用 {@link org.springframework.transaction.jta.JtaTransactionManager}
 * 且引入 spring-boot-starter-jta-atomikos 包
 *
 * @author Kevin
 * @date 2023-10-25 21:26
 */
@AutoConfiguration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "hk.multi-ds", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(value = {MultiDataSourceProperties.class})
public class MultiDatabaseAutoConfiguration {

    private final MultiDataSourceProperties multiDataSourceProperties;

    @Bean
    @ConditionalOnMissingBean(value = {UserTenantAware.class})
    public UserTenantAware tenantInfoHolder() {
        return new DefaultTenantInfoAware(multiDataSourceProperties.getDataSources().keySet().iterator().next());
    }

    /**
     * 默认以第一个数据库为 主数据库
     */
    @Bean
    public DynamicDataSource dynamicDataSource(UserTenantAware userTenantAware) throws Exception {
        var targetDataSource = new HashMap<>();
        DataSource defaultDataSource = null;
        var dataSourceMap = multiDataSourceProperties.getDataSources();
        if (Objects.nonNull(dataSourceMap)) {
            for (var entry : dataSourceMap.entrySet()) {
                var dataSource = entry.getValue();
                dataSource.afterPropertiesSet();
                var ds = dataSource.initializeDataSourceBuilder()
                        .build();
                if (Objects.isNull(defaultDataSource)) {
                    defaultDataSource = ds;
                }
                targetDataSource.put(entry.getKey(), ds);
            }
        }
        AssertUtils.notNull(defaultDataSource, "Default DataSource must not be null.");
        return new DynamicDataSource(userTenantAware, defaultDataSource, targetDataSource);
    }

    /**
     * @param tenantIdResolvers 注意排序
     */
    @Bean
    public DataSourceAspect dataSourceAspect(UserTenantAware userTenantAware,
                                             ObjectProvider<List<TenantIdResolver>> tenantIdResolvers) {
        List<TenantIdResolver> sourceTypeResolvers = tenantIdResolvers.getIfAvailable();
        List<TenantIdResolver> resolvers = new ArrayList<>();
        if (Objects.nonNull(sourceTypeResolvers)) {
            resolvers.addAll(sourceTypeResolvers);
        }
        addDefaultTenantIdResolvers(resolvers);
        var compositeDataSourceTypeResolver = new CompositeTenantIdResolver();
        compositeDataSourceTypeResolver.registerResolvers(resolvers);
        return new DataSourceAspect(userTenantAware, compositeDataSourceTypeResolver);
    }

    // 添加两个默认的
    private void addDefaultTenantIdResolvers(List<TenantIdResolver> resolvers) {
        if (ClassUtils.isPresent("com.hk.core.authentication.api.SecurityContextUtils", null)) {
            List<TenantIdResolverProvider> resolverProviders = new ArrayList<>();
            resolverProviders.add(new PrincipalTenantResolverProvider());
            if (CollectionUtils.isNotEmpty(multiDataSourceProperties.getUserNameTenantIdMap())) {
                resolverProviders.add(new PrincipalAccountResolverProvider().setAccountTenantIdMap(multiDataSourceProperties.getUserNameTenantIdMap()));
            }
            resolvers.add(new UserPrincipalTenantIdResolver(resolverProviders));
        }
        resolvers.add(new RequestUriResolver().setRequestUriTenantIdMap(Collections.emptyMap())); // Request URI
        resolvers.add(TenantIdResolver.fixed()); // Fixed
    }

}
